package com.jinmdz.fmis.api.api.service;

import com.jinmdz.fmis.api.api.model.common.IdVersionData;
import com.jinmdz.fmis.api.api.model.settlement.ChargeItemData;
import com.jinmdz.fmis.api.api.model.settlement.ChargeRefundData;
import com.jinmdz.fmis.api.base.BaseService;
import com.jinmdz.fmis.api.constant.MessageConst;
import com.jinmdz.fmis.api.model.config.dictionary.BusinessLogCode;
import com.jinmdz.fmis.api.model.config.dictionary.DictionaryCode;
import com.jinmdz.fmis.api.model.config.dictionary.OperationLogCode;
import com.jinmdz.fmis.api.model.system.UserItem;
import com.jinmdz.fmis.api.wrapper.ChargeWrapper;
import com.jinmdz.fmis.api.wrapper.OperationWrapper;
import com.jinmdz.fmis.common.ext.StringBuilderExt;
import com.jinmdz.fmis.common.util.DataUtil;
import com.jinmdz.fmis.core.base.BaseResult;
import com.jinmdz.fmis.core.exception.ActionException;
import com.jinmdz.fmis.core.util.JacksonUtil;
import com.jinmdz.fmis.dao.dao.ChargeItemDao;
import com.jinmdz.fmis.dao.dao.FuneralBusinessDao;
import com.jinmdz.fmis.dao.model.funeral.FuneralBusinessViewItem;
import com.jinmdz.fmis.mapper.entity.*;
import com.jinmdz.fmis.mapper.mapper.ChargeItemMapper;
import com.jinmdz.fmis.mapper.mapper.ChargeRefundMapper;
import com.jinmdz.fmis.mapper.mapper.ChargeSettleMapper;
import com.jinmdz.fmis.mapper.mapper.ChargeSettlePayMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;

/**
 * ChargeSettlementRefundService类
 *
 * @author LiCongLu
 * @date 2020-05-13 10:52
 */
@Service("chargeSettlementRefundService")
public class ChargeSettlementRefundService extends BaseService {

    @Resource
    private FuneralBusinessService funeralBusinessService;

    @Resource
    private FuneralBusinessDao funeralBusinessDao;

    @Resource
    private ChargeItemMapper chargeItemMapper;

    @Resource
    private ChargeSettleMapper chargeSettleMapper;

    @Resource
    private ChargeSettlePayMapper chargeSettlePayMapper;

    @Resource
    private OperationLogCode operationLogCode;

    @Resource
    private BusinessLogCode businessLogCode;

    @Resource
    private DictionaryCode dictionaryCode;

    @Resource
    private OperationWrapper operationWrapper;

    @Resource
    private ChargeWrapper chargeWrapper;

    @Resource
    private ChargeRefundMapper chargeRefundMapper;

    @Resource
    private ChargeItemDao chargeItemDao;

    /**
     * 结算退费，即增加一条负值结算记录
     *
     * @param userItem 当前帐号
     * @param data     结算主键集合
     * @return
     * @author LiCongLu
     * @date 2020-02-20 16:36
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResult saveChargeSettleToRefund(UserItem userItem, ChargeRefundData data) throws ActionException {
        String businessCode = data.getBusinessCode();
        // 通过业务编码获取殡葬信息
        FuneralBusinessViewItem funeralItem = funeralBusinessDao.getFuneralBusinessViewByBusinessCode(businessCode);
        if (DataUtil.isNull(funeralItem) || DataUtil.isNull(funeralItem.getId(), funeralItem.getVersion())) {
            throw exception(MessageConst.NO_FUNERAL_CODE);
        }

        // 被退费信息
        IdVersionData refundItem = data.getRefundItem();

        // 获取被退费信息
        ChargeItemEntity chargeEntity = chargeItemMapper.getChargeItemById(refundItem.getId());
        if (DataUtil.isNull(chargeEntity) || DataUtil.isNull(chargeEntity.getId(), chargeEntity.getVersion())) {
            return failure("主键错误，不存在此费用信息");
        }

        // 验证费用版本
        if (!DataUtil.equals(chargeEntity.getVersion(), refundItem.getVersion())) {
            throw exception(versionError("费用信息"));
        }

        // 验证业务编码
        if (!chargeEntity.getBusinessCode().equals(businessCode)) {
            throw exception("请求业务编码与原业务编码不匹配");
        }

        // 验证是否结算
        if (DataUtil.invalid(chargeEntity.getAsSettled())) {
            throw exception("此费用未计算，无法进行退费");
        }

        // 验证是否退费
        if (DataUtil.valid(chargeEntity.getAsRefund())) {
            throw exception("此费用为退费关联费用，无法进行退费");
        }

        // 验证是否业务费用
        if (DataUtil.valid(chargeEntity.getAsBusiness())) {
            throw exception("此费用为业务费用，无法进行修改");
        }

        // 验证是否有打印发票
        ChargeSettleEntity chargeSettleEntity = chargeSettleMapper.getChargeSettleById(chargeEntity.getChargeSettleId());
        if (DataUtil.valid(chargeSettleEntity.getAsInvoice())) {
            throw exception("此费用已打印发票，无法进行退费");
        }

        // 执行保存退费
        saveChargeSettleToRefund(userItem, funeralItem, data, chargeEntity, chargeSettleEntity);

        return success("退费成功");
    }

    /**
     * 执行退费保存数据
     *
     * @param userItem           当前账号
     * @param funeralItem        殡葬业务
     * @param data               退费请求数据
     * @param chargeEntity       被退费信息
     * @param chargeSettleEntity 被退费结算信息
     * @param
     * @return
     * @author LiCongLu
     * @date 2020-05-13 09:17
     */
    private void saveChargeSettleToRefund(UserItem userItem, FuneralBusinessViewItem funeralItem, ChargeRefundData data, ChargeItemEntity chargeEntity, ChargeSettleEntity chargeSettleEntity) throws ActionException {
        String businessCode = data.getBusinessCode();
        // 总应收金额
        BigDecimal receiptCharge = new BigDecimal(0);
        // 总优惠金额
        BigDecimal discountCharge = new BigDecimal(0);
        // 总惠民金额
        BigDecimal benefitCharge = new BigDecimal(0);
        // 总实际收入金额
        BigDecimal totalCharge = new BigDecimal(0);

        // 退费负值费用主键
        Integer refundMinusId = 0;
        // 退费新费用主键
        Integer refundNewId = 0;

        // 当前时间
        Date nowDate = nowDate();

        // 零数据
        BigDecimal decimalZero = new BigDecimal(0);

        //  产生负值费用信息
        ChargeItemEntity minusEntity = getEntity(userItem, ChargeItemEntity.class);
        // 保存负费用
        saveChargeSettleToRefundMinusCharge(chargeEntity, businessCode, nowDate, decimalZero, minusEntity);

        // 记录负值费用主键
        refundMinusId = minusEntity.getId();

        // 累加负值费用
        receiptCharge = receiptCharge.add(minusEntity.getItemCharge());
        discountCharge = discountCharge.add(minusEntity.getDiscountCharge());
        benefitCharge = benefitCharge.add(minusEntity.getBenefitCharge());
        totalCharge = totalCharge.add(minusEntity.getRealityCharge());

        // 新费用信息记录
        ChargeItemData newData = data.getChargeItem();
        if (newData != null && newData.getItemCharge() != null &&
                newData.getItemCharge().compareTo(decimalZero) > 0) {
            // 当新费用记录应收金额大于零时，才记录信息
            ChargeItemEntity newEntity = getEntity(userItem, ChargeItemEntity.class);
            // 保存新费用
            saveChargeSettleToRefundNewCharge(chargeEntity, businessCode, nowDate, newData, newEntity);

            // 记录新费用主键
            refundNewId = newEntity.getId();

            // 累加新费用
            receiptCharge = receiptCharge.add(newEntity.getItemCharge());
            discountCharge = discountCharge.add(newEntity.getDiscountCharge());
            benefitCharge = benefitCharge.add(newEntity.getBenefitCharge());
            totalCharge = totalCharge.add(newEntity.getRealityCharge());
        }

        // 退费结算费用信息
        ChargeSettleEntity refundSettleEntity = getEntity(userItem, ChargeSettleEntity.class);
        refundSettleEntity.setAsRefund(1)
                .setBusinessCode(businessCode)
                .setRandomCode(DataUtil.getUUID())
                // 默认为收费
                .setSettleTypeCode(dictionaryCode.getSettleTypeTuiFei())
                .setSettleUserId(userItem.getId())
                // 生成结算批次编号
                .setSettleBatchNo(funeralBusinessService.getSettleBatchNoAsNew())
                .setReceiptCharge(receiptCharge)
                .setDiscountCharge(discountCharge)
                .setBenefitCharge(benefitCharge)
                .setSettleCharge(totalCharge)
                .setSettleTime(nowDate)
                .setAsRefund(1)
                .setAsVerify(0);

        // 添加执行结算
        chargeSettleMapper.insertChargeSettle(refundSettleEntity);
        // 判断结算结果
        if (DataUtil.invalid(refundSettleEntity.getId())) {
            throw exception("保存退费结算失败");
        }

        // 保存结算付款与退费记录
        saveChargeSettleToRefundPay(userItem, chargeEntity, refundMinusId, refundNewId, nowDate, refundSettleEntity);

        // 更新被退费等退费状态
        chargeEntity.setAsRefund(1);
        chargeItemMapper.updateChargeItem(chargeEntity);
        chargeSettleEntity.setAsRefund(1);
        chargeSettleMapper.updateChargeSettle(chargeSettleEntity);

        // 更新负费用结算及新费用结算主键
        chargeItemDao.updateForSettleRefund(businessCode, refundSettleEntity.getId(), refundMinusId, refundNewId, userItem.getId());

        // 添加退费记录日志信息
        saveChargeSettleToRefundLog(userItem, funeralItem, data, chargeEntity, refundSettleEntity);
    }

    /**
     * 保存退费负费用信息
     *
     * @param chargeEntity 被退费实体
     * @param businessCode 业务编码
     * @param nowDate      当前时间
     * @param decimalZero  零实体
     * @param minusEntity  负费实体
     * @return
     * @author LiCongLu
     * @date 2020-05-13 10:12
     */
    private void saveChargeSettleToRefundMinusCharge(ChargeItemEntity chargeEntity, String businessCode, Date nowDate, BigDecimal decimalZero, ChargeItemEntity minusEntity) throws ActionException {
        // 设置负值信息
        minusEntity.setBusinessCode(businessCode)
                .setServiceItemId(chargeEntity.getServiceItemId())
                .setItemPrice(chargeEntity.getItemPrice())
                .setChargeTime(nowDate)
                .setAsBusiness(0)
                .setAsRefund(1)
                .setRefundId(chargeEntity.getId())
                .setRandomCode(chargeEntity.getRandomCode())
                .setRemark("退费负值");
        // 设置负值
        minusEntity.setItemNumber(decimalZero.subtract(chargeEntity.getItemNumber()))
                .setItemUnit(chargeEntity.getItemUnit())
                .setDiscountCharge(decimalZero.subtract(chargeEntity.getDiscountCharge()))
                .setBenefitCharge(decimalZero.subtract(chargeEntity.getBenefitCharge()))
                .setItemCharge(decimalZero.subtract(chargeEntity.getItemCharge()))
                .setRealityCharge(decimalZero.subtract(chargeEntity.getRealityCharge()));

        // 插入负值费用
        chargeItemMapper.insertChargeItem(minusEntity);
        if (DataUtil.invalid(minusEntity.getId())) {
            throw exception("保存结算退费负值费用失败");
        }
    }

    /**
     * 保存退费新费用
     *
     * @param chargeEntity 被退费费用
     * @param businessCode 业务编码
     * @param nowDate      当前时间
     * @param newData      新费用数据
     * @param newEntity    新费用实体
     * @return
     * @author LiCongLu
     * @date 2020-05-13 10:17
     */
    private void saveChargeSettleToRefundNewCharge(ChargeItemEntity chargeEntity, String businessCode, Date nowDate, ChargeItemData newData, ChargeItemEntity newEntity) throws ActionException {
        newEntity.setBusinessCode(businessCode)
                .setServiceItemId(chargeEntity.getServiceItemId())
                .setChargeTime(nowDate)
                .setAsBusiness(0)
                // 新费用不标记退费，即可继续退费
                .setAsRefund(0)
                .setRefundId(chargeEntity.getId())
                .setRandomCode(chargeEntity.getRandomCode())
                .setRemark("退费新费用");

        // 设置新金额
        newEntity.setItemPrice(newData.getItemPrice())
                .setItemNumber(newData.getItemNumber())
                .setItemCharge(newData.getItemCharge())
                .setDiscountCharge(newData.getDiscountCharge())
                .setBenefitCharge(newData.getBenefitCharge())
                .setChargeTime(nowDate);

        // 判断比较实收金额与优惠惠民的金额
        chargeWrapper.checkChargeForEntity(newEntity);

        // 插入负值费用
        chargeItemMapper.insertChargeItem(newEntity);
        if (DataUtil.invalid(newEntity.getId())) {
            throw exception("保存结算退费新费用失败");
        }
    }

    /**
     * 保存退费付款信息
     *
     * @param userItem           当前账号
     * @param chargeEntity       被退费费用
     * @param nowDate            当前时间
     * @param refundMinusId      退费负费用主键
     * @param refundNewId        退费新费用主键
     * @param refundSettleEntity 退费结算数据
     * @return
     * @author LiCongLu
     * @date 2020-05-13 10:48
     */
    private void saveChargeSettleToRefundPay(UserItem userItem, ChargeItemEntity chargeEntity, Integer refundMinusId, Integer refundNewId, Date nowDate, ChargeSettleEntity refundSettleEntity) throws ActionException {
        // 退费付款信息
        ChargeSettlePayEntity refundPayEntity = getEntity(userItem, ChargeSettlePayEntity.class);
        refundPayEntity.setBusinessCode(refundSettleEntity.getBusinessCode())
                .setChargeSettleId(refundSettleEntity.getId())
                // 默认退费付款方式为现金
                .setPayTypeCode(dictionaryCode.getPayTypeXianJin())
                .setPayCharge(refundSettleEntity.getSettleCharge())
                .setPayTime(nowDate)
                .setRandomCode(refundSettleEntity.getRandomCode());

        // 添加结算付款信息
        chargeSettlePayMapper.insertChargeSettlePay(refundPayEntity);
        // 判断结算付款信息
        if (DataUtil.invalid(refundPayEntity.getId())) {
            throw exception("保存退费结算付款失败");
        }

        // 添加退费记录
        ChargeRefundEntity refundEntity = getEntity(userItem, ChargeRefundEntity.class);
        refundEntity.setBusinessCode(refundSettleEntity.getBusinessCode())
                .setChargeItemId(chargeEntity.getId())
                .setRefundCharge(refundSettleEntity.getSettleCharge())
                .setRefundMinusId(refundMinusId)
                .setRefundTime(nowDate);

        // 判断是否有新费用
        if (DataUtil.valid(refundNewId)) {
            refundEntity.setRefundNewId(refundNewId);
        }

        // 插入退费记录
        chargeRefundMapper.insertChargeRefund(refundEntity);
        // 判断退费插入结果
        if (DataUtil.invalid(refundPayEntity.getId())) {
            throw exception("保存退费结算记录失败");
        }
    }

    /**
     * 添加退费日志记录
     *
     * @param userItem           当前帐号
     * @param funeralItem        当前业务
     * @param data               请求数据
     * @param chargeEntity       被退费信息
     * @param refundSettleEntity 退费结算实体
     * @return
     * @author LiCongLu
     * @date 2020-05-13 11:15
     */
    private void saveChargeSettleToRefundLog(UserItem userItem, FuneralBusinessViewItem funeralItem, ChargeRefundData data, ChargeItemEntity chargeEntity, ChargeSettleEntity refundSettleEntity) throws ActionException {

        // 添加业务日志及操作日志
        String businessCode = funeralItem.getBusinessCode();
        Integer typeCode = businessLogCode.getChargeSettleRefund();
        FuneralBusinessLogEntity businessLogEntity = operationWrapper.businessLog(userItem, businessCode, typeCode);

        // 操作日志字符串
        StringBuilderExt builder = new StringBuilderExt();
        builder.format("结算费用退费，业务编码[{0}]，逝者姓名[{1}]，结算总额[{2}]，操作人员[{3}]"
                , businessCode, funeralItem.getDeadName(), DataUtil.getPlainString(refundSettleEntity.getSettleCharge()), userItem.getFullName());

        // 添加日志
        operationWrapper.operationLog(userItem, businessLogEntity, operationLogCode.getChargeSettle(), builder.toString()
                , JacksonUtil.obj2Json(data), JacksonUtil.obj2Json(chargeEntity), JacksonUtil.obj2Json(refundSettleEntity));
    }
}
